package com.zijidelu.luframework.base.bizsig;

import cn.hutool.core.lang.func.Func1;
import com.zijidelu.luframework.base.model.DataModel;
import com.zijidelu.luframework.constant.RepositoryConstant;
import com.zijidelu.luframework.constant.StringConstant;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * 业务签名接口。
 *
 * @author ZIJIDELU
 * @datetime 2025/4/17 23:27
 */
public interface BizSignature<D extends DataModel> {

    /**
     * 计算唯一键信息列表。
     *
     * @return 唯一键信息列表
     */
    List<FieldDescriptor<D>> calcFieldDescriptorList();

    /**
     * 获取唯一键信息列表。
     * <p>
     * 该方法会在第一次调用时初始化缓存，后续调用会直接从缓存中获取。
     *
     * @return 唯一键信息列表
     */
    default List<FieldDescriptor<D>> getFieldDescriptorList() {
        return BizSignatureCacheManager.getBizSignatureRecord(this)
                .getFieldDescriptorList(this::calcFieldDescriptorList);
    }

    /**
     * 获取唯一键字段列表。
     * <p>
     * 该方法会在第一次调用时初始化缓存，后续调用会直接从缓存中获取。
     *
     * @return 唯一键字段列表
     */
    default List<Func1<D, ?>> getBizSignatureFieldList() {
        return BizSignatureCacheManager.getBizSignatureRecord(this).getBizSigFieldList(
                () -> getFieldDescriptorList()
                        .stream()
                        .map(FieldDescriptor::getField)
                        .collect(Collectors.toList())
        );
    }

    /**
     * 获取唯一键字段列表。
     * <p>
     * 该方法会在第一次调用时初始化缓存，后续调用会直接从缓存中获取。
     *
     * @return 唯一键字段列表
     */
    default List<String> getBizSignatureFieldNameList() {
        return BizSignatureCacheManager.getBizSignatureRecord(this).getBizSigFieldNameList(
                () -> getFieldDescriptorList()
                        .stream()
                        .map(FieldDescriptor::getFieldName)
                        .collect(Collectors.toList())
        );
    }

    /**
     * 获取唯一键字段Map。
     * <p>
     * 该方法会在第一次调用时初始化缓存，后续调用会直接从缓存中获取。
     *
     * @return 唯一键字段Map
     */
    default Map<Func1<D, ?>, FieldDescriptor<D>> getBizSignatureFieldMap() {
        return BizSignatureCacheManager.getBizSignatureRecord(this).getBizSignatureFieldMap(
                () -> getFieldDescriptorList()
                        .stream()
                        .collect(Collectors.toMap(FieldDescriptor::getField, fd -> fd))
        );
    }

    /**
     * 获取唯一键字段名称Map。
     * <p>
     * 该方法会在第一次调用时初始化缓存，后续调用会直接从缓存中获取。
     *
     * @return 唯一键字段名称Map
     */
    default Map<String, FieldDescriptor<D>> getBizSignatureFieldNameMap() {
        return BizSignatureCacheManager.getBizSignatureRecord(this).getBizSignatureFieldNameMap(
                () -> getFieldDescriptorList().stream()
                        .collect(Collectors.toMap(FieldDescriptor::getFieldName, fd -> fd))
        );
    }

    /**
     * 业务签名校验。
     * <p>
     * 该方法会校验DTO中所有唯一键字段是否为空，如果存在空值字段，则会抛出异常。
     *
     * @param data data
     */
    default String validateBizSignature(D data) {
        // 获取所有唯一键字段，如果存在值为空的字段，则返回空值的字段名称
        return validateBizSignatureAndConsume(data, null);
    }

    /**
     * 业务签名校验消费器
     *
     * @param data          data
     * @param fieldConsumer 字段消费者
     * @return 业务签名字符串
     */
    default String validateBizSignatureAndConsume(D data, BiConsumer<FieldDescriptor<D>, Object> fieldConsumer) {
        Objects.requireNonNull(data, "Data can not be null");
        final boolean hasKeyConsumer = fieldConsumer != null;

        // 获取所有唯一键字段，如果存在值为空的字段，则返回空值的字段名称
        final StringJoiner missing = new StringJoiner("|");
        for (FieldDescriptor<D> fieldDescriptor : getFieldDescriptorList()) {
            Object extractorValue = fieldDescriptor.getField().callWithRuntimeException(data);
            if (extractorValue == null) {
                missing.add(fieldDescriptor.getNullStyle());
                continue;
            }

            if (hasKeyConsumer) {
                fieldConsumer.accept(fieldDescriptor, extractorValue);
            }
        }

        if (missing.length() > 0) {
            throw new BizSignatureException("[BizSignature]: errorMsg=字段值为空 nullField=" + missing);
        }

        return calcBizSignature(data);
    }

    /**
     * 计算业务签名字段值Map。
     * <p>
     * 业务签名字段->数据值列表
     *
     * @param dataColl 数据集合
     * @return 业务签名字段值Map
     */
    default Map<Func1<D, ?>, Collection<Object>> calcFieldGroupValues(Collection<D> dataColl) {
        // 构建业务签名字段Map
        final Map<Func1<D, ?>, Collection<Object>> bizSigFieldMap = new HashMap<>();
        for (D data : dataColl) {
            // 获取Data中所有业务签名字段值
            validateBizSignatureAndConsume(
                    data,
                    (k, v) -> bizSigFieldMap.computeIfAbsent(k.getField(), key -> new ArrayList<>()).add(v)
            );
        }

        return bizSigFieldMap;
    }

    /**
     * 计算业务签名字段名称Map。
     * <p>
     * 业务签名字段名称->数据值列表
     *
     * @param dataColl 数据集合
     * @return 业务签名字段名称Map
     */
    default Map<String, Collection<Object>> calcFieldNameGroupValues(Collection<D> dataColl) {
        // 构建业务签名字段Map
        final Map<String, Collection<Object>> bizSigFieldMap = new HashMap<>();
        for (D data : dataColl) {
            // 获取Data中所有业务签名字段值
            validateBizSignatureAndConsume(
                    data,
                    (k, v) -> bizSigFieldMap.computeIfAbsent(k.getFieldName(), key -> new ArrayList<>()).add(v)
            );
        }

        return bizSigFieldMap;
    }

    /**
     * 计算业务签名数据Map。
     * <p>
     * 业务签名->数据
     *
     * @param dataColl 数据集合
     * @return 业务签名数据Map
     */
    default Map<String, D> calcBizSignatureDataMap(Collection<D> dataColl) {
        return dataColl.stream().collect(Collectors.toMap(this::calcBizSignature, e -> e));
    }

    /**
     * 计算业务签名字符串
     *
     * @param data data
     * @return 业务签名字符串
     */
    default String calcBizSignature(D data) {
        StringJoiner joiner = new StringJoiner(RepositoryConstant.BIZ_SIGNATURE_SPLIT);
        for (FieldDescriptor<D> fieldDescriptor : getFieldDescriptorList()) {
            joiner.add(fieldDescriptor.getField().callWithRuntimeException(data).toString());
        }

        return joiner.toString();
    }

    /**
     * 计算业务签名LOG样式
     *
     * @param data data
     * @return 业务签名LOG样式
     */
    default String calcBizSignatureLogStyle(D data) {
        String prefix = "[BizSignature]: signature=";
        String suffix = "";
        StringJoiner joiner = new StringJoiner("|", prefix, suffix);
        validateBizSignatureAndConsume(data, (k, v) -> joiner.add(k.calcStyle(v)));

        return joiner.toString();
    }

    /**
     * 计算业务签名样式
     *
     * @param data data
     * @return 业务签名样式
     */
    default String calcBizSignatureStyle(D data) {
        StringJoiner joiner = new StringJoiner(StringConstant.CH_SEMICOLON, StringConstant.EMPTY, StringConstant.SPACE);
        validateBizSignatureAndConsume(data, (k, v) -> {
            final String style = "(" + k.getFieldNameAlias() + ")" + v.toString();
            joiner.add(style);
        });

        return joiner.toString();
    }

}
